﻿using System;
using System.Collections.Generic;
using System.Linq;
using Se.Common;
using Se.Log;
using Se.Model.Host;
using System.IO;
using System.Reflection;


namespace Se.Model.GMCommand
{
    /// <summary>
    /// GM Command Manager.
    /// </summary>
    public sealed class GMCommandManager
    {
        /// <summary>
        /// GM指令头
        /// </summary>
        public const string c_GMCommand_Prefix = "/GM";
        /// <summary>
        /// GM命令，消息分解的基本长度
        /// </summary>
        public const int c_GMCommand_MessageSplit_BaseLength = 2;

        /// <summary>
        /// 所有命令集合
        /// </summary>
        HashSet<string> _AllCommandSet = new HashSet<string>();


        private GMCommandModes _CommandMode = GMCommandModes.Off;
        /// <summary>
        /// Command mode.
        /// </summary>
        public GMCommandModes CommandMode
        {
            get
            {
                return _CommandMode;
            }
            set
            {
                _CommandMode = value;
            }
        }
        /// <summary>
        /// Command collection.
        /// </summary>
        static Dictionary<string, ExecuteGMCommand> _CommandSet = new Dictionary<string, ExecuteGMCommand>(1024);

        /// <summary>
        /// Register commands of a module.
        /// </summary>
        /// <param name="module">模块对象</param>
        public void RegisterCommand(object module)
        {
            if (CommandMode == GMCommandModes.Off)
                return;

            // Command methods are private.
            var methods = module.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (var method in methods)
            {
                var attr = method.GetCustomAttributes(typeof(GMCommandAttribute), false).FirstOrDefault() as GMCommandAttribute;
                if (attr == null)
                    continue;
                if (string.IsNullOrEmpty(attr.CommandLine))
                    continue;

                var func = Delegate.CreateDelegate(typeof(ExecuteGMCommand), module, method) as ExecuteGMCommand;
                if (func == null)
                    continue;

                if (!_CommandSet.ContainsKey(attr.CommandLine.ToLower()))
                    _CommandSet.Add(attr.CommandLine.ToLower(), func);
            }
        }

        /// <summary>
        /// 收集所有的GM命令
        /// </summary>
        /// <param name="searchPattern">筛选模式</param>
        public void CollectAllGMCommand(string searchPattern)
        {
            if (CommandMode == GMCommandModes.Off)
                return;

            // 这里手动添加进程，按入口进程的地址查找，通常情况下，都会在相同目录下          
            string location = Directory.GetCurrentDirectory();
            var files = Directory.GetFiles(location, searchPattern);
            foreach (var file in files)
            {
                var asm = Assembly.LoadFile(file);
                foreach (var type in asm.GetTypes())
                {
                    var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
                    foreach (var method in methods)
                    {
                        var attr = method.GetCustomAttributes(typeof(GMCommandAttribute), false).FirstOrDefault() as GMCommandAttribute;
                        if (attr == null)
                            continue;
                        if (string.IsNullOrEmpty(attr.CommandLine))
                            continue;

                        if (!_AllCommandSet.Contains(attr.CommandLine.ToLower()))
                            _AllCommandSet.Add(attr.CommandLine.ToLower());
                        else
                            SeLog.Error("GMCommandManager->CollectAllGMCommand: The GM Command {0} has duplicated, from Method {1} "
                                , attr.CommandLine.ToLower(), method.Name);
                    }
                }
            }
        }

        /// <summary>
        /// 检查GM命令是否存在
        /// </summary>
        /// <param name="commandName">命令名称</param>
        /// <param name="findInAll">是否从全部范围内查找，true为在全部命令中查找，false为在已注册命令中查找</param>
        /// <returns>找到返回true, 否则false</returns>
        public bool IsGMCommandExisted(string commandName, bool findInAll = true)
        {
            if (findInAll)
                return _AllCommandSet.Contains(commandName);
            else
                return _CommandSet.ContainsKey(commandName);
        }

        /// <summary>
        /// 解析GM命令参数
        /// </summary>
        /// <param name="messages">消息内容，带有/GM头</param>
        /// <returns>返回命令参数数组，若无效命令返回null，若无参数返回空数组</returns>
        public string[] ResolveCommandParameters(string messages)
        {
            var aryStr = messages.Split(' ');
            // 分为二段（无参数）和三段（有参数），否则视为无效
            if (aryStr.Length != 3 && aryStr.Length != 2)
                return null;

            // 最后段为命令名称
            // 示例: /GM CommandName Parameters
            if (aryStr.Length == 3)
                return aryStr[2].Split(',');
            else if (aryStr.Length == 2)
                return new string[0];

            return null;
        }

        /// <summary>
        /// 解析命令名称
        /// </summary>
        /// <param name="messages">命令行，带/GM头</param>
        /// <returns>返回命令名称，若不合法则返回Empty</returns>
        public string ResolveCommandName(string messages)
        {
            var aryStr = messages.Split(' ');
            // 分为二段（无参数）和三段（有参数），否则视为无效
            if (aryStr.Length != 3 && aryStr.Length != 2)
                return string.Empty;

            // 中间段为命令名称
            // 示例: /GM CommandName Parameters
            return aryStr[1];
        }

        /// <summary>
        /// Execute the GM Command.
        /// </summary>
        /// <param name="session"></param>
        /// <param name="identity"></param>
        /// <param name="messages"></param>        
        /// <returns></returns>
        public void ExecuteCommand(SocketSession session, string messages)
        {
            // Resolve the string
            var commandName = ResolveCommandName(messages);
            if (string.IsNullOrEmpty(commandName))
            {
                SeLog.Error("GMCommandManager->ExecuteCommand 无效的命令");
                return;
            }
            var paramList = ResolveCommandParameters(messages);
            if (paramList == null)
            {
                SeLog.Error(string.Format("无效的命令 {0}", commandName));
                return;
            }

            // Find the command and execute it.
            ExecuteGMCommand executeDelegate;
            _CommandSet.TryGetValue(commandName, out executeDelegate);
            if (executeDelegate == null)
            {
                SeLog.Error(string.Format("命令 {0} 未注册", commandName));
                return;
            }

            // Execute the command.
            if (!executeDelegate(session, paramList))
            {
                SeLog.Error(string.Format("命令 {0} 执行失败", commandName));
            }

        }


    }   // class GMCommandManager

}   // namespace BlackTeam.Business.Generic.GMCommand
